<template>
  <div class="employees-container">
    <div class="app-container">
      <!-- 通用工具栏组件使用 全局组件-->
      <page-tools>
        <!-- 自定义左侧内容 -->
        <template #slot-left>
          <span>总共 {{ total }} 条记录</span>
        </template>

        <!-- 自定义右侧内容 -->
        <template #slot-right>
          <el-button v-power="'import'" type="danger" size="small" @click="uploadExFn">批量导入员工excel</el-button>
          <el-button v-power="'export'" type="success" size="small"@click="handleDownload">导出全部员工excel</el-button>
          <el-button type="primary" size="small" @click="addEmployeeFn">新增单个员工</el-button>
        </template>
      </page-tools>
      <!-- 卡片 -->
      <el-card style="margin-top: 10px;">
        <!-- 表格 -->
        <el-table border :data="employeesList">
          <el-table-column label="序号" type="index"/>
          <el-table-column label="姓名" prop="username"/>
          <el-table-column label="头像" prop="staffPhoto">
            <!-- <template slot-scope="scope">
             <el-image v-if="scope.row.staffPhoto" :src="scope.row.staffPhoto" fit="cover"></el-image>
             <el-image v-else src="https://www.h5w3.com/wp-content/uploads/2020/10/5f950f3b002a2.jpg">
             </el-image>
            </template> -->
            <!-- 加载成功时展示的图片 -->
            <template slot-scope="scope">
             <el-image :src="scope.row.staffPhoto" fit="cover" style="text-align: center;">
               <!-- 加载失败时展示的图片 -->
               <template #error>
                <el-image src="https://www.h5w3.com/wp-content/uploads/2020/10/5f950f3b002a2.jpg" />
                 <span style="font-size: 12px;">加载失败</span>
               </template>
             </el-image>
            </template>
          </el-table-column>
          <el-table-column label="手机号" prop="mobile"/>
          <!-- 开启排序模式 -->
          <el-table-column label="工号" prop="workNumber" default-sort sortable :sort-method="workNumberSortFn"/>
          <el-table-column label="聘用形式" prop="formOfEmployment">
            <!-- 插槽优先级大于prop数据显示的优先级 -->
            <!--
                作用域插槽:
                1 使用插槽技术时,
                2 需要组件内变量 -> 作用域插槽

                在组件内,用<slot 属性名="组件内变量值"></slot>
                (table-column写好了内部叫row)
                在插槽内,用<template v-slot="变量名(随意起)">,变量会手机slot身上属性和值形成对象
             -->
            <template slot-scope="scope">
                {{ employeeFormOfEmploymentFn(scope.row) }}
            </template>
          </el-table-column>
          <el-table-column label="部门" prop="departmentName"/>
          <el-table-column label="入职时间" prop="timeOfEntry" :formatter="timeFormatter"/>
          <el-table-column label="操作" width="280">
            <template v-slot="scope">
              <el-button type="text" size="small" @click="employeeDetail(scope.row)">查看</el-button>
              <el-button type="text" size="small" @click="assignRoleFn(scope.row)">分配角色</el-button>
              <el-button type="text" size="small" @click="del(scope.row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
        <!-- 分页组件 -->
        <el-row type="flex" justify="center" align="middle" style="height: 60px">
          <!-- 分页区域 -->
          <!-- 父组件 current-page.sync
               子组件内 emit('update:current-page',最新的值)
               由于 current-page.sync 右边等于号内的值是父组件内的值,该父组件的值 = 子组件传递过来的值(最新的值)
           -->
          <el-pagination :current-page.sync="query.page" :page-sizes="[1,5,10]" :page-size.sync="query.size"
            layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange"
            @current-change="handleCurrentChange" />
        </el-row>
      </el-card>
      <!-- dialog -->
      <el-dialog title="新增员工"
      :visible="showDialog"
      :show-close="false"
      :close-on-press-escape="false"
      :close-on-click-modal="false"
      @close="dialogCloseFn"
      >
        <!-- 表单对象 -->
        <!--
            show 是props属性,子组件接不接收都可以
            子组件派发 update:show,值 即可
            show 传递过去的是父组件的变量(showDialog)
            而 父组件的变量(showDialog)的值 = 子组件传递过来的值(直接赋值)
         -->
        <emp-form ref="empform"
          :departentList="departentList"
          :show.sync="showDialog"
          @addEmployeeEV="addEmployeeEV"
        />
      </el-dialog>

      <!-- 员工-分配角色权限 - 弹窗 -->
      <el-dialog title="分配角色" :visible.sync="showRoleDialog">
        <!-- 设置角色组件 -->
        <!-- show只拿来sync使用,子组件不接收 -->
        <assign-role-dialog ref="roleDialog" :show.sync="showRoleDialog" :assignRoleList="assignRoleList" @setEmployeeFn="setEmployeeFn"/>
      </el-dialog>
    </div>
  </div>
</template>
<script>
  import empForm from './component/empForm.vue'
  import assignRoleDialog from './component/assignRoleDialog'
  import { getEmployeesListAPI, getDepartmentListAPI, getUserPhotoAPI,
  AddEmployeeAPI, delEmployeeAPI, getRoleListAPI, setEmployeeRoleAPI } from '@/api'
  import employeeFormOfEmployment from './formatter.js' // 枚举后台返回的0/1渲染出文本,属性名转换枚举
  import dayjs from 'dayjs' // 处理时间格式
  import { transTree } from '@/utils/handleTreeData.js'
  export default {
    name: 'EmployeesIndex',
    data() {
      return {
        query: {
          page: 1, // 页码
          size: 10 // 每页条数
        },
        employeesList: [], // 部分员工列表(展示用)
        allEmployeesList: [], // 所有员工列表(excel用)
        assignRoleList: [], // 权限角色列表(子组件用)
        total: 0, // 数据总条数
        clickRowRoleId: '', // 保存点击分配权限时当前行的id(发请求用)
        showDialog: false, // add employee dialog control
        showRoleDialog: false, // assign role dialog control
        departentList: [], // 部门信息列表树形结构
        filename: '文件名', // 导出 excel的文件名
        autoWidth: true, // excel自适应宽度?
        bookType: 'xlsx', // 导出 excel的后缀?
      }
    },
    created() {
      this.getEmployeesListFn() // 获取员工列表
      this.getDepartmentListFn() // 获取组织部门列表
      this.getRoleListFn() // 获取角色身份列表
    },
    methods: {
      // 获取组织结构列表数据(添加员工选择部门时使用)
      async getDepartmentListFn(){
        const res = await getDepartmentListAPI()
        this.departentList = transTree(res.data.depts,'')
        // console.log(res)
      },
      // 获取员工列表(部分)
      async getEmployeesListFn(){
        const res = await getEmployeesListAPI(this.query)
        this.employeesList = res.data.rows
        this.total = res.data.total
      },
      // 获取员工列表(全部)
      async getAllEmployeesListFn(){
        const res = await getEmployeesListAPI({
          page: 1,
          size: this.total // 获取所有员工(excel导出使用)
        })
        this.allEmployeesList = res.data.rows
        this.total = res.data.total
      },
      // 获取所有权限角色列表(传递给子组件用)
      async getRoleListFn(){
        const res = await getRoleListAPI({
          page: 1,
          pageSize: 10
        })
        // 获取所有后台数据而又不知道后台有多少数据的时候
        const resAll = await getRoleListAPI({
          page: 1,
          pageSize: res.data.total
        })
        this.assignRoleList = resAll.data.rows
      },
      // 新增员工功能(自定义事件)
      async addEmployeeEV(employeeObj){
         const res = await AddEmployeeAPI(employeeObj)
         this.getEmployeesListFn() // 获取员工列表
         this.$message('添加员工成功')
         // console.log(res)
      },
      // 删除员工
      del(row){
          // 显示删除询问对话框
          this.$confirm('此操作将永久删除该角色, 是否继续?', '提示', {
              confirmButtonText: '确定',
              cancelButtonText: '取消',
              type: 'warning'
            }).then(async confirm => {
              await delEmployeeAPI(row.id)
              // 删完数据后,再次刷新页面之前判断,当前页数据存储数组是否还有数据
              if(this.employeesList.length <= 1){
                this.query.page-- // 请求上一页的数据
                if(this.query.page <= 0){ // 当删除完第一页的最后一条数据时
                  this.query.page = 1 // 强制让请求的页码写为1,不能为0
                }
              }
              this.$message('删除成功')
              this.getEmployeesListFn() // 获取员工列表
            }).catch(err => {
              this.$message('取消操作','info')
            })
      },
      // 查看员工详情按钮
      employeeDetail(row){
        // console.log(row)
        // 跳转时传递id查询角色使用,传递聘用形式字段,因为跳转后请求的数据里没有该字段
        this.$router.push(`/employees/detail?id=${row.id}&form=${row.formOfEmployment}`)
      },
      // 分配角色按钮
      async assignRoleFn(row){
        this.clickRowRoleId = row.id // 中转变量
        // 获取当前选中角色的角色权限列表
        const res = await getUserPhotoAPI(row.id)
        // dialog 展开
        this.showRoleDialog = true
        /*
          知识点: Vue更新DOM的动作是异步的
          上一句话同步代码,会把所有同步代码执行完毕,才会更新真实DOm(dialog组件才会出现)
          假如立刻获取弹窗内容组件可能获取不到(执行同步代码时dialog还没创建了,拿不到该组件,更别谈访问内部变量)
          解决: 用 Vue 提供的 $nextTick()方法
          作用: 等待异步 DOM 更新完毕后, vue会回调()里函数体执行
        */
        this.$nextTick(() => {
          // 待 dialog 组件渲染完成后给子组件,CheckBox绑定的数组赋值,即可完成回显
          this.$refs.roleDialog.checkList = res.data.roleIds
        })
      },
      // 自定义事件 -> 给当前行员工赋予/移除角色权限
      async setEmployeeFn(roleList){
        await setEmployeeRoleAPI({
          id: this.clickRowRoleId, // 当前选中行id
          roleIds: roleList // 权限id数组
        })
        this.$message('分配角色权限成功')
      },
      // 聘用形式 -> 数据处理
      employeeFormOfEmploymentFn(row){
        let obj = employeeFormOfEmployment.hireType.find(item => item.id == row.formOfEmployment)
        return obj ? obj.value : '未知' // 所有obj值为undefined,返回未知,否则 undefined.value会报错
      },
      // 工号排序 (自定义排序方法,不用内置的默认方法(内置默认方法排序是按 ASCII 码进行对比比较的,因为值都是字符串)
      workNumberSortFn(a,b){
        // a 和 b 是表单内的每一个对象 row
        // 数组sort方法: fn(a,b) 小于0 , 那么 a 会被排列到b前面,不动 a-b
        // 数组sort方法: fn(a,b) 等于0 , 那么 a 和 b 位置不变,不动 a-b
        // 数组sort方法: fn(a,b) 大于0 , 那么 b 会被排列到 a 之前,调转位置 a-b
        // console.log(a.workNumber,'-',b.workNumber,Number(a.workNumber) - Number(b.workNumber))
        return Number(a.workNumber) - Number(b.workNumber)
      },
      // dialog 弹出层关闭时触发的事件
      dialogCloseFn(){
        // this -> Employee 当前组件对象
        // this.$refs.empform -> empform 组件对象(好比Employeeform内this的值)
        // this.$refs.empform.$refs.addForm -> 就是 el-form 组件对象
        this.$refs.empform.$refs.addForm.resetFields()
      },
      // 时间格式处理
      timeFormatter(row){
        return dayjs(row.timeOfEntry).format('YYYY-MM-DD')
      },
      // 添加员工按钮
      addEmployeeFn(){
        this.showDialog = true // 展示 dialog
      },
      // 每页显示的条数发生改变时触发(使用了sync修饰符无需手动赋值给query内的参数)
      handleSizeChange(newSize) {
        this.query.page = 1 // 改值让其请求第一页的数据,而不是当前点击页的数据
        this.getEmployeesListFn() // 获取员工列表
      },

      // 当前页面发生改变时触发(使用了sync修饰符无需手动赋值给query内的参数)
      handleCurrentChange(newPage) {
        this.getEmployeesListFn() // 获取员工列表
      },
      // 点击上传excel事件
      uploadExFn(){
        this.$router.push('/excel')
      },
      // 导出excel事件 -> 下载带数据的 excel 文件(自动)
      async handleDownload(){
        await this.getAllEmployeesListFn() // 请求所有员工数据
        // import() 方法,webpack提供的一种模块引入的方法(也可以写在外面script模块内)
        // 类似于懒加载,点击导出按钮的时候,才去引入对应 js
        // import() 方法
        // 参数: 要引入的包名/路由
        // 返回值: Promise对象
        import('@/vendor/Export2Excel').then(excel => {
          let excelHeaderKey = ['序号','姓名','头像','手机号','工号','聘用形式','部门','入职时间']
          // 核心: 把请求回来数组套对象的数据,转成只有值的二维数组, [{},{}] -> [[],[]]
          // 表格文件的列标题(数组类型)
          const tHeader = excelHeaderKey // 写死的excel表头
          // 表格里第二行开始的正文数据(二维数组)
          // table值的获取 Objetc.values() -> 传入对象,返回所有值的数组,有排序问题
          // 缺点: 该 dataRes 获取的值是一组无序的数组值(导致:导出的excel标题和文本内容对不上)
          /*
              发现的问题: 数据和头部纵向对应不上
              原因: Object.values() 取出的值 无序的,没办法和 tHeader 按顺序对应
              解决: 想尽一切办法,让我们的值在数组里顺序和 tHeader 顺序对应上
              表格里和页面上只用到了8个对象属性,而数组对象里有10对 k&v (没关系,最后取出的值是undefined会被忽略)
          */
          // 解决:
          // 2 遍历每个英文对象
         let dataRes = this.allEmployeesList.map((obj,index) => {
            let arr = []  // 按顺序保存值
            // 3 遍历每个中文 key 值
            excelHeaderKey.forEach(zhKey => {
              // 序号在遍历的时候,就有索引值了,所以它的值不用去英文对象里获取,所以不用映射
              if(zhKey == '序号'){
                arr.push(index + 1)
              }else{
                // 1 中文 -> 英文的映射关系(枚举)
                let enKey = employeeFormOfEmployment.zhTransEn[zhKey] // 英文名字换取
                // 用枚举后的英文名字获取相对应的 obj员工大对象内的value
                let enValue = obj[enKey]
                // 在添加数据前处理聘用形式的格式
                if(enKey == 'formOfEmployment'){ // 如果 key 为 招聘形式
                // 枚举里面映射值
                 let mappingObj  = employeeFormOfEmployment.hireType.find(obj => obj.id == Number(enValue))
                 let mappingValue = mappingObj ? mappingObj.value : '未知' // 匹配到对象后,转换,得到转换后的值
                 arr.push(mappingValue)
                }else{
                  // 按顺序添加
                  arr.push(enValue)
                }
              }
            })
            // 4 把一行值数组 return 给 map 内
            return arr
          })
          // 其中内层数据里直接写行的内容值,key已经在tHeader头部写了
          const data = dataRes
          // console.log(data)
          // 该方法excel.export_json_to_excel 是 @/vendor/Export2Excel.js 组件内的导出方法
          excel.export_json_to_excel({
            header: tHeader, // 表头 必填
            data, // 具体数据 必填
            filename: this.filename, // 文件名称
            autoWidth: this.autoWidth, // 宽度是否自适应
            bookType: this.bookType // 生成的文件类型
          })
        })
      }
    },
    mounted() {},
    computed: {},
    components: {
      empForm,
      assignRoleDialog
    }
  }
</script>
<style lang="less" scope>

</style>
